De evolutie van de productiviteit en salarissen van gemiddelde NBA-spelers#
Moad Matoug, 14433672
Bart van der Lee, 14464306
Thom Varela Nunes, 14609096
Melih Metin, 13993208
Groep O3
Introductie
Tijdens dit project zullen we kijken naar de (geavanceerde) statistieken van spelers in de National Basketball Association (NBA) en de veranderingen in hun salarissen. De verzamelde gegevens beginnen in het jaar 2000 en eindigen in 2017. Met behulp van deze gegevens zullen we allereerst verschillen visualiseren in wat er van de gemiddelde speler wordt verwacht. Dit zal worden gedaan door te kijken naar het gemiddelde van bepaalde (essentiële) statistieken in elk seizoen. Vervolgens zullen we kijken naar het gemiddelde salaris van deze spelers en het verschil in zowel productiviteit als salaris analyseren. Het doel is om een beter inzicht te krijgen of NBA spelers daadwerkelijk beter worden, en zo ja, of ze eerlijk gecompenseerd worden.
Basketbal is een teamsport die wordt gespeeld tussen twee teams van vijf spelers. Het doel van het spel is om de bal in de tegenstander zijn basket te gooien en zo punten te scoren. Het spel bestaat uit vier kwarten van elk twaalf minuten en het team met de meeste punten aan het einde van de wedstrijd wint.
We zullen het in dit project ook een paar basketbal termen gebruiken, om alvast bekend te worden met een paar van de belangrijkste leggen we er hier een paar alvast uit.
Punten: Dit is het totale aantal punten dat een speler of team scoort tijdens een wedstrijd. In het basketbal is het mogelijk om in één keer 1, 2 of 3 punten te scoren. 2 punten score je door de bal in het net te krijgen terwijl je binnen de 3-punt lijn staat en 3 punten score je door de bal in het net te krijgen terwijl je buiten de 3-punt lijn staat. 1 punt is te halen met een ‘free throw’, dit is een vrije worp die vanaf ongeveer 4,5 meter wordt genomen. Tegenstanders mogen niet in de buurt komen van de speler die de free throw neemt, vandaar de naam. Een free throw wordt gegeven als er een overtreding wordt gemaakt tegen de speler met de bal.
Rebounds: Dit verwijst naar het totale aantal rebounds dat een speler of team pakt. Een rebound is het bemachtigen van de bal nadat een schot is gemist. Rebounds kunnen offensief zijn (na een gemiste poging van het eigen team) of defensief (na een gemiste poging van de tegenstander).
Assists: Dit is het aantal keren dat een speler een pass geeft die resulteert in een score door een medespeler. Assists meten de mate waarin een speler zijn teamgenoten in staat stelt te scoren.
Steals: Dit verwijst naar het aantal keren dat een speler de bal steelt van een tegenstander, waardoor zijn eigen team de balbezit krijgt.
Blocks: Dit is het aantal keren dat een speler een schotpoging van de tegenstander blokkeert en voorkomt dat de bal de basket bereikt.
Field Goal Percentage (FG%): Dit is de verhouding tussen het aantal gemaakte veldgoals en het aantal veldpogingen van een speler of team. Het geeft de efficiëntie aan van het schieten van het team of de speler.
Statistieken zijn een belangrijk deel van basketbal, vandaar dat er ook zoveel worden bijgehouden. Alle statistieken die wij in dit onderzoek zullen gebruiken geven volgens Gómez et al. (2008) goed weer of de productiviteit van spelers veranderd is en in welke mate het verandert is. Tot slot zijn de grafieken bijgesteld op inflatie voor optimale resultaten in de vergelijking.
Dataset en preprocessing
De originele datasets zijn gratis online beschikbaar op de websites Kaggle, Github en Data world. Eén van de datasets bevatte geavanceerde statistieken per individuele NBA-speler gedurende de periode 2000-2017. De tweede dataset gaf de salarissen van NBA-spelers gedurende de periode 2000-2020, desondanks werd alleen de periode 2000-2017 gebruikt wegens de geavanceerde statistieken. De derde dataset gaf de RAPTOR aan van verschillende NBA-spelers wegens het belang van de statistiek om de perspectieven te kunnen beargumenteren. Ten slotte gaf de laatste dataset inflatie in de Verenigde Staten per dag aan. De datasets die te maken hadden met NBA-statistieken en salarissen werden gecombineerd op basis van de naam en seizoen, wat leidde tot één uiteindelijke dataset met NBA gerelateerde statistieken. De dataset over inflatie werd geïmplementeerd door het gemiddelde salaris per basketbal-seizoen (jaar) aan te passen op de gemiddelde consumentenprijsindex (CPI)
Perspectieven
Er verschillende discussies ontstaan binnen basketbal waarbij men twee polariserende perspectieven heeft. De ene groep zegt namelijk dat de gemiddelde speler niet beter is geworden, maar dat hun salaris vooralsnog omhoog is gegaan. Aan de andere kant zeggen tegenstanders van dat standpunt dat basketbal, net als veel andere sporten, een snel evoluerende sport is waarbij de tempo en de vaardigheden van spelers constant groeit en dat hun salarisverhoging niet gelijk is aan de toename in vaardigheden en productie.
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
import pandas as pd
import numpy as np
df = pd.read_csv('https://raw.githubusercontent.com/bartvdlee/Informatievisualisatie/main/mergedfinal.csv')
df_cpi = pd.read_csv('https://raw.githubusercontent.com/bartvdlee/Informatievisualisatie/main/inflation.csv')
df_cpi['CPI'] = df_cpi['Oct']
df_cpi = df_cpi[['Year', 'CPI']]
df_cpi = df_cpi[df_cpi['Year'] > 1999]
df_cpi = df_cpi[df_cpi['Year'] < 2018]
df_salary = df[['season', 'salary']].groupby('season').mean()
df_salary['Year'] = df_salary.index
df_adjusted = pd.merge(df_salary, df_cpi, on='Year')
base_year_cpi = df_adjusted.loc[df_adjusted['Year'] == 2017, 'CPI'].values[0]
df_adjusted['adjusted salary'] = df_adjusted['salary'] * base_year_cpi / df_adjusted['CPI']
positions = ['SF', 'SG', 'C', 'PG', 'PF']
filtered_df = df[df['Pos'].isin(positions)]
# Create the box plot
fig = go.Figure()
for position in positions:
position_df = filtered_df[filtered_df['Pos'] == position]
fig.add_trace(
go.Box(
y=position_df['salary'],
name=position
)
)
# Update the layout
fig.update_layout(
title='Salary Distribution by Position (Positions: SF, SG, C, PG, PF)',
xaxis_title='Position',
yaxis_title='Salary'
)
# Show the plot
fig.show()
Hierboven staat een boxplot met de salarissen van elke positie in basketbal. Hier is het dus ook mogelijk om het verschil tussen posities te zien. Zo krijgen centers en power forwards gemiddeld het hoogste salaris. Centers en power forwards zijn de grotere spelers op het veld en van hen worden met name punten en rebounds verwacht door middel van hun lengte. Verder is te zien dat de outliers van de small forward en de shooting guard het hoogst zijn. Bij deze posities is het scoren van punten in het algemeen belang indien de speler een contributie wil hebben voor het team. De positie met het laagste gemiddelde salaris volgens de boxplot zijn point guards, wiens taak vooral bestaat uit het passen van de bal om de andere posities zoveel mogelijk te helpen scoren (NBA, 2016).
df_2000 = df[df['season'] == 2000]
df_2006 = df[df['season'] == 2006]
df_2011 = df[df['season'] == 2011]
df_2017 = df[df['season'] == 2017]
fig = go.Figure()
fig.add_trace(go.Box(y=df_2000['USG%'], name='2000'))
fig.add_trace(go.Box(y=df_2006['USG%'], name='2006'))
fig.add_trace(go.Box(y=df_2011['USG%'], name='2011'))
fig.add_trace(go.Box(y=df_2017['USG%'], name='2017'))
fig.update_layout(title='Box Plots of Usage Rate % for Different Years',
yaxis_title='USG%',
xaxis_title='Year')
fig.show()
Een van de andere statistieken waar naar gekeken kan worden is de “usage rating” ofwel het gebruikspercentage. Gebruikspercentage is een statistiek die wordt gebruikt om de mate van betrokkenheid van een speler bij het aanvallende spel van zijn team te meten. Het geeft aan welk percentage van zijn speelminuten een speler gebruikt om daadwerkelijk invloed te hebben op het spel. Er worden verschillende punten bijgehouden zoals aantal schoten, assists en nog veel meer, dit wordt vervolgens bij elkaar opgeteld en gedeeld door het aantal balbezit van zijn team. In het kort, een hoge gebruikspercentage betekent dat een speler veel invloed heeft op de aanval van zijn team en een lage gebruikspercentage betekent dat een speler juist weinig invloed heeft op de aanval van zijn team.
Hierboven is een boxplot grafiek te zien van het gebruikspercentage van een aantal jaren tussen 2000-2017. Er is dus geen duidelijk verschil tussen deze grafieken, dit betekent dat de gebruikspercentages ongeveer gelijk zijn gebleven voor de gemiddelde spelers door de jaren heen. Wat wel opvalt is dat er in 2017 veel meer outliers zijn dan de jaren daarvoor (de outliers worden gerepresenteerd op de grafiek door middel van een punt). Gedurende dat specifieke seizoen werd van een klein aantal spelers meer aanvallende contributie verwacht in verhouding tussen de voorgaande jaren, maar omdat het alleen voor outliers geldt kan er niet geconcludeerd worden dat speler Je zou dus kunnen zeggen dat de gemiddelde speler in de NBA een hogere aanvallende contributie gaf.
scatter_fig = px.scatter(df, x='salary', y='PER', color='Pos',
title='Salary vs. PER', hover_data=['name', 'Tm'])
scatter_fig.update_layout(xaxis_title='Salary', yaxis_title='PER')
scatter_fig.show()
Om te weten of spelers beter zijn geworden is het belangrijk om te weten hoe efficiënt spelers zijn. Een veel gebruikte manier om dit te meten is de Player Efficiency Rating (PER). Bij de PER wordt er gekeken naar hoe efficiënt een speler is tijdens een wedstrijd, statistieken zoals geraakte worpen en gemiste worpen spelen hier een rol, maar ook statistieken zoals rebounds en assists worden in mindere mate betrokken.
In de grafiek hierboven is het gemiddelde salaris van NBA spelers en de gemiddelde PER van NBA spelers geplot. Zoals is te zien is er een best duidelijk verband tussen op uitzondering van het jaar 2000. Het is dus tot op zekere hoogte mogelijk om te stellen dat de efficiëntie van een speler invloed heeft op hun salaris, en dat spelers wel gecompenseerd worden voor hun effectiviteit. Verder geven de posities (sommige spelers spelen meerdere posities) een beeld van de efficiëntie van spelers die een bepaalde positie spelen.
fig = px.scatter(df, x="PTS", y="salary", animation_frame="season", trendline="ols")
fig.update_layout(
title="Yearly Relationship between Points and Performance",
xaxis_title="PTS",
yaxis_title="Salary",
)
fig.show()
De grafiek ‘Yearly Relationship between Points and Performance’ toont per seizoen de verhouding tussen het salaris en de totale punten die een speler scoort in het seizoen. Aan de grafiek is op te merken dat er een positieve lineaire relatie is tussen de twee variabelen en dat de lijn die de trend vertoont in de latere jaren hoger en horizontaal ook verder kwam wat inhoudt dat spelers meer punten scoorden en daarnaast ook meer verdienden.
average_salary_bpm = df[['season', 'salary', 'BPM', 'raptor_total', 'WS/48']].groupby(by='season').mean()
adjusted = [n for n in df_adjusted['adjusted salary']]
average_salary_bpm['adjusted salary'] = adjusted
average_salary_bpm = average_salary_bpm.reset_index()
salary_values_per_million = average_salary_bpm['adjusted salary'] / 1000000
data = {
'season': average_salary_bpm['season'],
'BPM': average_salary_bpm['BPM'],
'salary': salary_values_per_million,
'raptor_total': average_salary_bpm['raptor_total'],
'WS/48': average_salary_bpm['WS/48']
}
df_salary_bpm = pd.DataFrame(data)
fig = make_subplots(specs=[[{"secondary_y": True}]])
# Add traces
# fig.add_trace(go.Scatter(x=df_salary_bpm['season'], y=df_salary_bpm['BPM'], name='BPM'), secondary_y=False)
fig.add_trace(go.Scatter(x=df_salary_bpm['season'], y=df_salary_bpm['raptor_total'], name='RAPTOR'),
secondary_y=False)
# fig.add_trace(go.Scatter(x=df_salary_bpm['season'], y=df_salary_bpm['WS/48'], name='WS/48'), secondary_y=False)
fig.add_trace(go.Scatter(x=df_salary_bpm['season'], y=df_salary_bpm['salary'], name='Mean Salary (in million dollars)'),
secondary_y=True)
# Add figure title
fig.update_layout(title_text="Mean RAPTOR and Mean Salary per Season")
# Set x-axis title
fig.update_xaxes(title_text="Season")
# Set y-axes titles
fig.update_yaxes(title_text="RAPTOR", secondary_y=False)
fig.update_yaxes(title_text="Mean Salary (per million dollars)", secondary_y=True)
fig.show()
De visualisatie van de data is een lijngrafiek die het gemiddelde salaris van een NBA speler per seizoen en de gemiddelde RAPTOR per seizoen bevat. De ‘Robust Algorithm (using) Player Tracking (and) On/Off Ratings’ is niet een statistiek dat gedurende een NBA-wedstrijd direct ontwikkeld kan worden, maar de statistiek bestaat uit een wiskundige formule om de contributie van een speler te meten. De meting richt zich, net als bijvoorbeeld de BPM, op de offensieve en verdedigende contributie van een speler per 100 bezittingen, vergeleken met een gemiddelde speler in de associatie. De grafiek toont aan dat de gemiddelde RAPTOR niet positief wordt, wat inhoudt dat de negatieve contributies in grotere mate aanwezig zijn vergeleken met de spelers met positieve contributies. Verder is er te zien dat er een indicatie is voor een correlatie tussen de gemiddelde RAPTOR en het gemiddelde salaris.
# Calculate average efficiency metrics per season
df_season_avg = df.groupby('season').agg({'2P%': 'mean', '2PA': lambda x: (x / df['G']).sum() / df['name'].nunique(),
'3P%': 'mean', '3PA': lambda x: (x / df['G']).sum() / df['name'].nunique(),
'name': 'nunique'}).reset_index()
# Creating subplots with shared y-axis
fig = make_subplots(rows=1, cols=2, shared_yaxes=True, subplot_titles=['Two-Point Attempts', 'Three-Point Attempts'])
# Adding the bar chart for Two-Point Attempts
fig.add_trace(go.Bar(
x=df_season_avg['season'],
y=df_season_avg['2PA'],
name='2PA',
), row=1, col=1)
# Adding the scatter plot for Two-Point Percentage
fig.add_trace(go.Scatter(
x=df_season_avg['season'],
y=df_season_avg['2P%'],
name='2P%',
mode='markers',
marker=dict(size=8),
), row=1, col=1)
# Adding the bar chart for Three-Point Attempts
fig.add_trace(go.Bar(
x=df_season_avg['season'],
y=df_season_avg['3PA'],
name='3PA',
), row=1, col=2)
# Adding the scatter plot for Three-Point Percentage
fig.add_trace(go.Scatter(
x=df_season_avg['season'],
y=df_season_avg['3P%'],
name='3P%',
mode='markers',
marker=dict(size=8),
), row=1, col=2)
# Updating layout
fig.update_layout(
title='Efficiency of Average Player per Season',
xaxis=dict(title='Season'),
yaxis=dict(title='Attempts'),
)
# Displaying the chart
fig.show()
De grafiek ‘Efficiency of Average Player per Season’ geeft per seizoen aan wat de gemiddelde hoeveelheid 2-punten en 3-punten worpen die spelers schoten per wedstrijd in een seizoen. Verder is ook het percentage waarin spelers deze worpen maken geïmplementeerd. Aan de grafieken is te zien dat de percentages waarin spelers de worpen scoren redelijk hetzelfde zijn gebleven terwijl de totale hoeveelheid worpen voor zowel 2-punten als 3-punten gestegen zijn.
grouped_df = df.groupby(['season', 'Tm'])['salary'].sum().reset_index()
# Creating a bar chart
fig = px.bar(grouped_df, x='season', y='salary', color='Tm', title='Total Salary by Team per Season')
fig.show()
Deze grafiek geeft per seizoen het totale salaris dat wordt uitgegeven in de NBA, waarbij de verschillende kleuren representatief zijn voor de verschillende teams. In de grafiek is een duidelijke trend te zien waarbij het bestede salaris in de associatie groeide, waarbij het in 2017 ongeveer zes keer zo groot was als in 2000.
# Grouping the dataframe by season and calculating the average MP/G and WS
df_season_avg = df.groupby('season').agg({'MP': 'sum', 'G': 'sum', 'WS': 'mean'}).reset_index()
df_season_avg['MP/G'] = df_season_avg['MP'] / df_season_avg['G']
# Plotting the scatter plot with color encoding and larger marker size
fig = px.scatter(df_season_avg, x='WS', y='MP/G', color='season',
labels={'WS': 'Win Shares', 'MP/G': 'Minutes per Game (MP/G)'},
title='Influence of Minutes per Game on Win Shares per Season',
size_max=50)
# Displaying the chart
fig.show()
In de grafiek ‘Influence of Minutes per Game on Win Shares per Season’ worden de gemiddelde minuten die een speler per wedstrijd per seizoen speelde vergeleken met de win shares. Win shares is een statistiek dat de individuele productiviteit van spelers opdeelt in de uiteindelijke productiviteit van het team van een speler. De spreidingsdiagram toont aan dat spelers rond 2000 een hogere win shares en minuten hadden ten opzichte van de latere jaren van de dataset.
Conclusie
De perspectieven
Persectief 1: De gemiddelde speler is niet beter geworden, maar hun salaris is vooralsnog omhoog gegaan.
Perspectief 2: Basketbal is net als veel andere sporten, een snel evoluerende sport waarbij het tempo en de vaardigheden van spelers constant groeit; en hun salarisverhoging is niet gelijk aan de toename in vaardigheden en productie.
Op basis van de verzamelde grafieken kan het volgende geconcludeerd worden. Ten eerste zijn de totale salarissen in grote mate gegroeid gedurende de periode 2000-2017 waarbij het totale salaris in 2000 net boven de 500 miljoen dollar was terwijl het in 2017 al boven de 3 miljard dollar was. Daarnaast bevat de data ook een aantal consistenties en veranderingen omtrent het productiviteit van spelers. Zo waren er meer outliers voor de gebruikerspercentage (usage rate) te zien in 2017 ten opzichte van de voorgaande jaren. Verder is de totale hoeveelheid worpen (2-punt worpen en 3-punt worpen) van de gemiddelde speler per wedstrijd gestegen terwijl het percentage om deze worpen te scoren gelijk is gebleven. Dit heeft uiteindelijk geleid tot meer punten en een hoger salaris.
Aan de andere kant toont de data dat er geen sterke correlatie is tussen de hoeveelheid punten die een speler in een seizoen scoort en zijn salaris. Alhoewel deze correlatie wel bestaat, is het niet sterk. Daarnaast is er geen correlatie tussen de efficiëntie van een speler en het salaris dat zij verdienen. Ook toont de data aan dat ondanks het kleine verschil in efficiëntie tussen verschillende posities er vooralsnog een verschil is in salaris. Tot slot geeft de data weer dat spelers in het begin van de periode 2000-2017 meer minuten speelden en een hoger deel hadden op het succes van hun team (win shares).
Uit de data is dus gebleken dat, afhankelijk van welke variabelen er naar worden gekeken, beide perspectieven te onderbouwen zijn. Er is daarom geen één juiste perspectief.
Werkverdeling
Gedurende het hele project hebben de auteurs er een prioriteit van gemaakt om ervoor te zorgen dat alle leden van de groep evenveel werk en inspanning zouden leveren en dat iedereen wist wat hij moest doen. De auteurs zijn erin geslaagd de last eerlijk te verdelen en duidelijk te maken wat iedereen geacht wordt te doen. Melih maakte de meeste visualisaties, Thom schreef het grootste deel van de tekst, Moad schreef een deel van de tekst, deed de preprocessing en verdeelde de taken, Bart maakte een deel van de visualisaties, deed de Github-kant van de dingen en schreef ook een deel van de tekst. Moad was tevens het wakend oog van de groep en de man met de visie.
Reflectie op feedback van de TA’s
Tijdens het project kregen de auteurs feedback van verschillende TA’s. De TA’s hebben ook veel vragen van de auteurs beantwoord. De auteurs hebben alle feedback erg serieus genomen en de suggesties van de TA’s geïmplementeerd. De reden voor deze houding tegenover feedback en voor het snel implementeren van de suggesties is dat feedback waardevolle inzichten en perspectieven biedt die kunnen helpen bij het verfijnen van vaardigheden, het verbeteren van prestaties en het verbeteren van relaties. De feedback van de TA’s was ook erg nuttig om bevestiging te krijgen dat de auteurs voldeden aan specifieke vereisten van de opdracht. De auteurs zijn ervan overtuigd dat de feedback van de TA’s de kwaliteit van ons eindproduct en de eerdere versies van het project sterk heeft verbeterd.
Reflectie op eigen feedback
Tijdens het project reflecteerden de auteurs op hun eigen werk en dat van de anderen. Moad was in het bijzonder onze wakend oog en Moad gaf veel feedback aan de andere auteurs. We behandelden de feedback van elkaar serieus en gaven de hoogste prioriteit aan het implementeren van elkaars suggesties. Naast het uiterst belangrijk achten van de feedback van anderen, gaven de auteurs ook feedback terug aan de anderen. Dit creëerde een omgeving waarin iedereen kon groeien en zijn beste beentje voor kon zetten. Het is dankzij deze positieve omgeving dat de auteurs naar elkaar toe gegroeid zijn als team en dat samenwerken een waar genot was.
Bronnenlijst
Gómez, M. I., Lorenzo, A., Barakat, R., Ortega, E., & Palao, J. M. (2008).
Differences in Game-Related Statistics of Basketball Performance by Game
Location for Men’s Winning and Losing Teams. Perceptual and Motor Skills, 106(1), 43–50. https://doi.org/10.2466/pms.106.1.43-50
NBA. (2016, 19 december). Basketball Positions - NBA.com: Jr. NBA. NBA.com: Jr. NBA. https://jr.nba.com/basketball-positions/